﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;

namespace DynaSpace.Entity
{
	/// <summary>
	/// Třída sloužící pro pohon entit a reakce na kolize
	/// </summary>
	public class CollisionEngine : ParticleEngine
	{	
		private Vector2 speedToAdd = Vector2.Zero;

		/// <summary>
		/// Updatuje pozici entity
		/// </summary>
		/// <param name="gameTime">Čas hry</param>
		/// <param name="entity">Entita k updatování</param>
		/// <param name="origSpeed">Rychlost</param>
		protected override void UpdatePosition(GameTime gameTime, EntityBase entity, ref Vector2 origSpeed)
		{
			// pokud je rychlost nulova, nebudeme s entitou pohybovat
			if(origSpeed == Vector2.Zero)
				return;

			Vector2 speed = origSpeed;
			if (entity.Parent.Map.CheckCollision(entity, entity.Position + speed))
			{
				// přesnost kroků bude 1 pixel
				float minSizeOfStep = 1f / EntityBase.SIZE.Length();

				// hodnota zpomalení tření
				float? frictionValueHorizontal = speed.Y > 0 ? FrictionBottom : FrictionTop;
				float? frictionValueVertical = speed.X > 0 ? FrictionRight : FrictionLeft;

				// horizontalne treni
				if(frictionValueHorizontal.HasValue)
				{
					speed.X *= 1 -  Math.Min(1, (float)gameTime.ElapsedGameTime.TotalSeconds * frictionValueHorizontal.Value / this.Weight);
				}

				// vertikalni treni
				if(frictionValueVertical.HasValue)
				{
					speed.Y *= 1 - Math.Min(1,(float)gameTime.ElapsedGameTime.TotalSeconds * frictionValueVertical.Value / this.Weight);
				}

				// pozici budeme upravovat v ramci lokalni promenne - do entity ji kvuli rychlosti zapiseme az posleze
				Vector2 position = entity.Position;

				// zjistit, zda je rychlost horizontální větší než vertikální a podle toho vytvoříme pole jednosložkových rychlostí
				bool horizontalFirst = Math.Abs(speed.X) > Math.Abs(speed.Y);
				Vector2[] remainSpeeds = { 
											 horizontalFirst ? new Vector2(speed.X, 0) : new Vector2(0, speed.Y),
											!horizontalFirst ? new Vector2(speed.X, 0) : new Vector2(0, speed.Y),
										 };

				// rychlost vynulujeme - znovu se nacte, pokud se v jednom ze smeru neprokaze kolize
				speed = Vector2.Zero; 

				// prozkoušet posun pohybu pro každý z nových jednosložkových vektorů
				for (int i = 0; i < 2; i++)
				{
					if (entity.Parent.Map.CheckCollision(entity, position + remainSpeeds[i]))
					{
						// hledat kolizi
						while (remainSpeeds[i].Length() > minSizeOfStep)
						{
							remainSpeeds[i] *= 0.5f; // snizit rychlost na polovinu
							if (!entity.Parent.Map.CheckCollision(entity, position + remainSpeeds[i]))
							{
								position += remainSpeeds[i];
							}
						}
					}
					else
					{
						// žádná kolize, posun je možný - zachováme zbytkovou rychlost a posuneme změnu pozice
						speed += remainSpeeds[i];
						position += speed;
					}
				}


				origSpeed = speed;
				entity.Position = position;
			}
			else
			{
				// žádná kolize, přidat plnou rychlost
				entity.Position += speed;
			}

			origSpeed += speedToAdd;
			speedToAdd = Vector2.Zero;
		}

		/// <summary>
		/// Pohne s objektem o určitý vektor
		/// </summary>
		/// <param name="moveVector"></param>
		public void Move(Vector2 moveVector, EntityBase entity)
		{
			// přesnost kroků bude 1 pixel
			float minSizeOfStep = 1f / EntityBase.SIZE.Length();

			// pozice entity
			Vector2 position = entity.Position;

			if (entity.Parent.Map.CheckCollision(entity, position + moveVector))
			{
				// hledat kolizi
				while (moveVector.Length() > minSizeOfStep)
				{
					moveVector *= 0.5f; // snizit rychlost na polovinu
					if (!entity.Parent.Map.CheckCollision(entity, position + moveVector))
					{
						position += moveVector;
					}
				}
			}
			else
			{
				position += moveVector;
			}

			// pohyb s entitou
			entity.Position = position;

			// přenést setrvačnost do rychlosti
			speedToAdd += MoveVectorToSpeed * moveVector;
		}

		/// <summary>
		/// Vrací nebo nastavuje jakou silou se při pohybu procedurou Move bude přenášet pohyb do rychlosti - setrvačnost
		/// Doporučené rozmezí 0 (žádný přenos) - 1 (plná setrvačnost).
		/// </summary>
		public float MoveVectorToSpeed { get; set; }

		/// <summary>
		/// Vrací nebo nastavuje tření horní hrany entity.
		/// Pokud je NULL, žádné tření nevzniká.
		/// </summary>
		public float? FrictionTop { get; set; }

		/// <summary>
		/// Vrací nebo nastavuje tření levá hrana entity.
		/// Pokud je NULL, žádné tření nevzniká.
		/// </summary>
		public float? FrictionLeft { get; set; }

		/// <summary>
		/// Vrací nebo nastavuje tření spodní hrany entity.
		/// Pokud je NULL, žádné tření nevzniká.
		/// </summary>
		public float? FrictionBottom { get; set; }

		/// <summary>
		/// Vrací nebo nastavuje tření pravé hrany entity.
		/// Pokud je NULL, žádné tření nevzniká.
		/// </summary>
		public float? FrictionRight { get; set; }

	}
}
